#pragma once
#include <utility>
#include <list>
#include <unordered_map>
#include <shared_mutex>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <algorithm>

#include "types and marcos.hpp"
#include "my_log.hpp"
#include "module signal.hpp"

// memory contral incomplete
// intercurrent contral incomplete
// user contral incomplete
// main controller unwritten

class memory_contral_block
{
public:
    memory_contral_block()
        : _parsing_location(0)
    {}

    ~memory_contral_block()
    {}

    memory_contral_block(const memory_contral_block& other) = delete;

    memory_contral_block(memory_contral_block&& other) noexcept
    {
        _block.swap(other._block);
        _parsing_location = other._parsing_location;
    }

    void push_back(std::pair<char*, size_t>& block)
    {
        while (!_lock.try_lock())
        {
            _wait.wait(_lock);
        }
        _block.push_back(block);
        _lock.unlock();
        _wait.notify_one();
    }

    //...
    std::pair<char*, size_t> pop_front()
    {
        while (!_lock.try_lock())
        {
            _wait.wait(_lock);
        }
        std::pair<char*, size_t> ret = _block.front();
        _block.pop_front();
        _lock.unlock();
        _wait.notify_one();
        return ret;
    }

    std::list<std::pair<char*, size_t>> _block;
    std::mutex _lock;
    std::condition_variable_any _wait;
    size_t _parsing_location;
};

class server_protocol
{
public:
    server_protocol()
    {
        //...
        char* memory_block = nullptr;
        for (int i = 0; i < MAX_MEMORY_BLOCK; ++i)
        {
            memory_block = new char[MAX_BLOCK_SIZE];
            _raw_block_storger.push_back(std::make_pair(memory_block, 0));
        }
        for (int i = 0; i < MAX_DGRAM; ++i)
        {
            memory_block = new char[MAX_DGRAM_LEN];
            _packed_block_storger.push_back(std::make_pair(memory_block, 0));
        }
    }

    ~server_protocol()
    {
        for (auto& i : _raw_block_storger)
        {
            delete[] i.first;
        }
        for (auto& i : _packed_block_storger)
        {
            delete[] i.first;
        }
    }

    void raw_data_receive(const userid_t& r_userid, std::pair<char*, size_t>& raw_data)
    {
        while (!_user_raw_list_lock.try_lock_shared())
        {
            _user_raw_list_wait.wait(_user_raw_list_lock);
        }
        _user_raw_data_list[r_userid].push_back(raw_data);
        _user_raw_list_lock.unlock_shared();
        _user_raw_list_wait.notify_one();
        update_id.store(r_userid);
    }

    //...
    bool memory_allocate(std::pair<char*, size_t>& ret, char which)
    {
        if (which == 'r')
        {
            while (!_raw_stor_lock.try_lock())
            {
                _raw_stor_wait.wait(_raw_stor_lock);
            }
            if (_raw_block_storger.size() == 0)
            {
                _raw_stor_lock.unlock();
                _raw_stor_wait.notify_one();
                return false;
            }
            ret = std::move(_raw_block_storger.back());
            _raw_block_storger.pop_back();
            _raw_stor_lock.unlock();
            _raw_stor_wait.notify_one();
        }
        else if (which == 'p')
        {
            while (!_packed_stor_lock.try_lock())
            {
                _packed_stor_wait.wait(_packed_stor_lock);
            }
            if (_packed_block_storger.size() == 0)
            {
                _packed_stor_lock.unlock();
                _packed_stor_wait.notify_one();
                return false;
            }
            ret = std::move(_packed_block_storger.back());
            _packed_block_storger.pop_back();
            _packed_stor_lock.unlock();
            _packed_stor_wait.notify_one();
        }
        return true;
    }

    //...
    void memory_release(std::pair<char*, size_t>& ret, char which)
    {
        if (which == 'r')
        {
            while (!_raw_stor_lock.try_lock())
            {
                _raw_stor_wait.wait(_raw_stor_lock);
            }
            _raw_block_storger.push_back(std::move(ret));
            _raw_stor_lock.unlock();
            _raw_stor_wait.notify_one();
        }
        else if (which == 'p')
        {
            while (!_packed_stor_lock.try_lock())
            {
                _packed_stor_wait.wait(_packed_stor_lock);
            }
            _packed_block_storger.push_back(std::move(ret));
            _packed_stor_lock.unlock();
            _packed_stor_wait.notify_one();
        }

    }

    //user protocol
    //r_userid, s_userid, len, coding, function, main message: UTF-8\UTF-16\UTF-32
    //stable part: r_userid-8bit, s_userid-8bit, len-16bit, coding-2bit, function-14bit, total: 6byte
    //unstable part: main message
    std::pair<char*, size_t> mosaic(const userid_t& r_userid)
    {
        while (!_user_raw_list_lock.try_lock_shared())
        {
            _user_raw_list_wait.wait(_user_raw_list_lock);
        }
        memory_contral_block& raw_data = _user_raw_data_list[r_userid];
        _user_raw_list_lock.unlock_shared();
        _user_raw_list_wait.notify_one();
        while (!raw_data._lock.try_lock())
        {
            raw_data._wait.wait(raw_data._lock);
        }
        size_t main_message_len = 0;
        if (static_cast<int>(raw_data._block.front().second - (raw_data._parsing_location + 4)) >= 0)
        {
            main_message_len = static_cast<size_t>(*(reinterpret_cast<short*>
                (raw_data._block.front().first + raw_data._parsing_location + 2)));
        }
        else
        {
            auto it = raw_data._block.begin();
            ++it;
            if (it == raw_data._block.end())
            {
                raw_data._lock.unlock();
                raw_data._wait.notify_one();
                return std::make_pair(nullptr, 0);
            }
            if (static_cast<int>(raw_data._block.front().second - (raw_data._parsing_location + 2)) <= 0)
            {
                main_message_len |= static_cast<size_t>(*(it->first - 
                    (raw_data._block.front().second - (raw_data._parsing_location + 2))));
                main_message_len <<= 8;
                main_message_len |= static_cast<size_t>(*(it->first + 1 -
                    (raw_data._block.front().second - (raw_data._parsing_location + 2))));
            }
            else
            {
                main_message_len |= static_cast<size_t>(*(raw_data._block.front().first + raw_data._parsing_location + 2));
                main_message_len <<= 8;
                main_message_len |= static_cast<size_t>(*(it->first));
            }
        }
        // divide
        {
            size_t total_len = main_message_len + 6;
            size_t left_len = total_len;
            for (auto i = raw_data._block.begin(); i != raw_data._block.end();)
            {
                if (i == raw_data._block.begin())
                {
                    left_len -= i->second - raw_data._parsing_location;
                    if (left_len <= 0)
                    {
                        //...
                        std::pair<char*, size_t> p_ret;
                        memory_allocate(p_ret, 'p');
                        std::copy(i->first + raw_data._parsing_location, i->first + i->second + left_len, p_ret.first);
                        raw_data._parsing_location = i->second + left_len;
                        if (left_len == 0)
                        {
                            std::pair<char*, size_t> r_ret = std::move(*i);
                            r_ret.second = 0;
                            memory_release(r_ret, 'r');
                            raw_data._block.erase(i);
                        }
                        raw_data._lock.unlock();
                        raw_data._wait.notify_one();
                        return p_ret;
                    }
                    else
                    {
                        ++i;
                    }
                }
                else
                {
                    left_len -= (*i).second;
                    if (left_len <= 0)
                    {
                        std::list<std::pair<char*, size_t>>::iterator memory_end = i;
                        ++memory_end;
                        std::pair<char*, size_t> p_ret;
                        p_ret.second = total_len;
                        size_t copy_start_fix = 0;
                        size_t block_count = 0;
                        memory_allocate(p_ret, 'p');
                        for (auto j = raw_data._block.begin(); j != memory_end; ++j)
                        {
                            ++block_count;
                            if (j == raw_data._block.begin())
                            {
                                std::copy(j->first + raw_data._parsing_location, j->first + j->second, p_ret.first + copy_start_fix);
                                copy_start_fix += j->second - raw_data._parsing_location;
                            }
                            else if (j == i)
                            {
                                std::copy(j->first, j->first + j->second + left_len, p_ret.first + copy_start_fix);
                            }
                            else
                            {
                                std::copy(j->first, j->first + j->second, p_ret.first + copy_start_fix);
                                copy_start_fix += j->second;
                            }
                        }
                        if (left_len < 0)
                        {
                            --block_count;
                        }
                        // divide
                        {
                            std::pair<char*, size_t> r_ret = raw_data._block.front();
                            for (size_t j = 0; j < block_count; ++j)
                            {
                                r_ret.second = 0;
                                memory_release(r_ret, 'r');
                                raw_data._block.pop_front();
                                if (j < block_count - 1)
                                {
                                    r_ret = raw_data._block.front();
                                }
                            }
                        }
                        raw_data._lock.unlock();
                        raw_data._wait.notify_one();
                        return p_ret;
                    }
                    else
                    {
                        ++i;
                    }
                }
            }
        }
        raw_data._lock.unlock();
        raw_data._wait.notify_one();
        return std::make_pair(nullptr, 0);
    }

    //...
private:
    // safe?
    std::shared_mutex _user_raw_list_lock;
    std::shared_mutex _user_packed_list_lock;
    std::condition_variable_any _user_raw_list_wait;
    std::condition_variable_any _user_packed_list_wait;
    std::unordered_map<userid_t, memory_contral_block> _user_raw_data_list;
    std::unordered_map<userid_t, memory_contral_block> _user_packed_data_list;
    std::mutex _raw_stor_lock;
    std::mutex _packed_stor_lock;
    std::condition_variable_any _raw_stor_wait;
    std::condition_variable_any _packed_stor_wait;
    std::list<std::pair<char*, size_t>> _raw_block_storger;
    std::list<std::pair<char*, size_t>> _packed_block_storger;
};